package com.eduardcapell.lopodder.beans;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import com.eduardcapell.lopodder.db.DBAccess;
import com.eduardcapell.lopodder.io.DownloadThread;
import com.eduardcapell.lopodder.xml.XMLUtils;

public class Feed implements Comparable {
	private int id;

	private URL url;
	private String name;
	private List<FeedElement> elements;
	private String folder = null;
	private boolean applyID3Tags = true;
	private int checkInterval = 30;
	private String renameFileTo = null; // Will add timestamp to name of file.
	private BasicID3Tag id3 = null;
    private FeedStatus status = null;
    
	public BasicID3Tag getId3() {
		return id3;
	}

	public void setId3(BasicID3Tag id3) {
		this.id3 = id3;
	}

	public String getRenameFileTo() {
		return renameFileTo;
	}

	public void setRenameFileTo(String renameFileTo) {
		this.renameFileTo = renameFileTo;
	}


	private boolean loadedElements = false;

	public boolean isLoadedElements() {
		return loadedElements;
	}

	public void setLoadedElements(boolean loadedElements) {
		this.loadedElements = loadedElements;
	}

	public Feed(String url, String folder) throws MalformedURLException {
		try {
			setUrl(new URL(url));
		} catch (MalformedURLException e) {
			e.printStackTrace();
		}
		
		setFolder(folder);
		fetchData();
	}
	
	public Feed(int id, String name, String url, String folder) throws MalformedURLException {
		try {
			setUrl(new URL(url));
		} catch (MalformedURLException e) {
			e.printStackTrace();
		}
		
		setFolder(folder);
		setName(name);
		setId(id);
	}
	
	public boolean isApplyID3Tags() {
		return applyID3Tags;
	}

	public void setApplyID3Tags(boolean applyID3Tags) {
		this.applyID3Tags = applyID3Tags;
	}

	public int getCheckInterval() {
		return checkInterval;
	}

	public void setCheckInterval(int checkInterval) {
		this.checkInterval = checkInterval;
	}

	
	

	public int getId() {
		return id;
	}


	public void setId(int id) {
		this.id = id;
	}

	
	
	public String getFolder() {
		return folder;
	}


	public void setFolder(String folder) {
		this.folder = folder;
	}


	public Feed() {
		System.out.println("Feed()");
	}
	
	public void fetchData() {
		fetchTitle();
	}
	
	private void fetchTitle() {
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		Document doc = null;
		try {
			DocumentBuilder db = dbf.newDocumentBuilder();
			doc = db.parse(getUrl().toString());
			doc.getDocumentElement().normalize();
			setName(XMLUtils.feedTitle(doc));
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	
	public void fetchItems() {
		List<FeedElement> elements = XMLUtils.findFeedElements(this);
		Iterator<FeedElement> it = elements.iterator();
		
		
		if (getElements() == null) {
			setElements(new ArrayList<FeedElement>());
		}
		
		while (it.hasNext()) {
			FeedElement fe = it.next();
			if (!(getElements().contains(fe))) {
				this.elements.add(fe);
				DBAccess.addElementToFeed(this, fe);
			} else {
//				System.out.println("Element in list: " + fe.toString());
			}
		}
		
		// Now, I have to check if some of the existing feeds should be deleted.
		it = getElements().iterator();
		List<FeedElement> toRemove = new ArrayList<FeedElement>();
		while (it.hasNext()) {
			FeedElement fe = it.next();
			if (!elements.contains(fe)) {
				toRemove.add(fe);
			}
		}
		
		getElements().removeAll(toRemove);
		DBAccess.removeElementsFromFeed(this, toRemove);
		
	}

	public void preloadItems() {
		
	}

	public void setElements(List<FeedElement> elements) {
		this.elements = elements;
	}

	public URL getUrl() {
		return url;
	}

	public void setUrl(URL url) {
		this.url = url;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public List<FeedElement> getElements() {
		return elements;
	}

	public void addElement(FeedElement element) {
		elements.add(element);
	}

	@Override
	public boolean equals(Object o) {
		if (o == null || !(o instanceof Feed)) {
			return false;
		}

		Feed f = (Feed) o;
		if (f.getUrl() == null) {
			return getUrl() == null;
		}
		
		return f.getUrl().equals(getUrl());
	}

	@Override
	public String toString() {
		return getId() + " - " + getUrl().toString();
	}

	
	@Override
	public int hashCode() {
		if (getUrl() == null) {
			return -1;
		}
		return getUrl().hashCode();
	}

	
	public void deleteFeedElementsObservers() {
		Iterator<FeedElement> it = elements.iterator();
		while (it.hasNext()) {
			it.next().deleteObservers();
		}
	}
	
	
	public static void downloadAllNew(List<Feed> feeds) {
		Iterator<Feed> it = feeds.iterator();
		List<FeedElement> toDownload = new ArrayList<FeedElement>();
		
		while (it.hasNext()) {
			Feed f = it.next();
			if (f.getFolder() == null || f.getFolder().length() == 0) {
				System.out.println("Feed has no folder!");
				continue;
			}
			Iterator<FeedElement> elems = f.getElements().iterator();
			while (elems.hasNext()) {
				FeedElement fe = elems.next();
				if (!fe.isDownloaded()) {
					toDownload.add(fe);
				}
			}
		}
		
		
		try {
			// TODO How do I make JVM to wait until thread is finished.
			Thread t = DownloadThread.getInstance(toDownload);
			t.start();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}

	
	@Override
	public int compareTo(Object o) {
		if (o == null || !(o instanceof Feed)) {
			return 1;
		}
		
		Feed f = (Feed) o;

		if (getName() == null) {
			return -1;
		}
		
		if (f.getName() == null) {
			return 1;
		}
		
		return getName().compareTo(f.getName());
	}

    /**
     * @return the status
     */
    public FeedStatus getStatus() {
        return status;
    }

    /**
     * @param status the status to set
     */
    public void setStatus(FeedStatus status) {
        this.status = status;
    }
}
